<!-- Background Animation -->
<style>
    @media (max-width: 960px) {
        #canvas {
            display: none;
        }
    }

    #canvas {
        position: fixed;
        top: 0;
        bottom: 0;
        right: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: -1;
        display: none;
    }
</style>
<canvas id="canvas"></canvas>
{% include controller-bar.html %}

<script>
    window.onload = function () {
        ParticleEffect.run();
        // test();
    };
    // 实时获取窗口大小 和 从缓存中获取窗口大小 的性能对比
    function test() {
        let cache = {
            width: 1024,
            height: 780
        };

        function test(executeFunc, times) {
            let start, end, num = times;
            start = new Date();
            while (times--) {
                executeFunc();
            }
            end = new Date();
            console.log(executeFunc.name + ' executes ' + num + 'times and takes ' + (end.getTime() - start.getTime()) /
                1000 + 's.');
        }

        function getWindowSizeRealTime() {
            return {
                width: window.innerWidth || document.documentElement.clientWidth,
                height: window.innerHeight || document.documentElement.clientHeight
            };
        }

        function getWindowSizeFromCache() {
            return cache;
        }
        [1000, 10000, 100000, 1000000].forEach(function (times) {
            test(getWindowSizeRealTime, times);
            test(getWindowSizeFromCache, times);
        });
    }


    // 粒子特效
    let ParticleEffect = {
        ctx: null,
        canvas: null,
        particles: [],
        mouseCoordinates: {
            x: 0,
            y: 0
        },
        config: {
            id: 'canvas', //
            count: 150, // 默认创建粒子数量
            radius: 1, // 默认粒子半径
            vxRange: [-1, 1], // 默认粒子横向移动速度范围
            vyRange: [-1, 1], // 默认粒子纵向移动速度范围
            scaleRange: [.5, 1], // 默认粒子缩放比例范围
            lineLenThreshold: 125, // 默认连线长度阈值
            // color: 'rgba(255,255,255,.2)'   
            color: 'rgba(235,235,235,1)' // 默认粒子、线条的颜色
            // color: 'lightskyblue'
        },
        init: function (newConfig) {

            // 更新config配置
            newConfig && Object.keys(newConfig).forEach(function (key) {
                _this.config[key] = newConfig[key];
            });

            let _this = this;
            this.canvas = document.getElementById(this.config.id);
            this.ctx = this.canvas.getContext('2d');

            // 只有在浏览器支持canvas的情况下才有效
            if (this.ctx) {

                Utils.updateWindowSize();
                let windowSize = Utils.getWindowSize();

                // 设置canvas宽高
                this.canvas.width = windowSize.width;
                this.canvas.height = windowSize.height;

                // 生成粒子
                let times = this.config.count;
                this.particles = [];
                while (times--) {
                    this.particles.push(new Particle({
                        x: Utils.rangeRandom(this.config.radius, windowSize.width - this.config
                            .radius),
                        y: Utils.rangeRandom(this.config.radius, windowSize.height - this.config
                            .radius),
                        vx: Utils.rangeRandom(this.config.vxRange[0], this.config.vxRange[1]),
                        vy: Utils.rangeRandom(this.config.vyRange[0], this.config.vyRange[1]),
                        color: this.config.color,
                        scale: Utils.rangeRandom(this.config.scaleRange[0], this.config.scaleRange[
                            1]),
                        radius: this.config.radius
                    }));
                }

                // 监听鼠标的mouseMove事件，记录下鼠标的x,y坐标
                window.addEventListener('mousemove', this.handleMouseMove.bind(this), false);

                // 监听窗口大小改变事件
                window.addEventListener('resize', this.handleWindowResize.bind(this), false);

                // 兼容requestAnimationFrame
                Utils.supportRequestAnimationFrame();
                window.requestAnimationFrame(this.draw.bind(this));
            }
        },
        move: function () {

            let windowSize = Utils.getWindowSize();

            this.particles.forEach(function (item) {

                // 更新粒子坐标
                item.x += item.vx;
                item.y += item.vy;

                // 如果粒子碰到了左墙壁或右墙壁，则改变粒子的横向运动方向
                if ((item.x - item.radius < 0) || (item.x + item.radius > windowSize.width)) {
                    item.vx *= -1;
                }

                // 如果粒子碰到了上墙壁或下墙壁，则改变粒子的纵向运动方向
                if ((item.y - item.radius < 0) || (item.y + item.radius > windowSize.height)) {
                    item.vy *= -1;
                }
            });
        },
        draw: function () {

            let _this = this;
            let lineLenThreshold = this.config.lineLenThreshold;
            let windowSize = Utils.getWindowSize();

            // 每次重新绘制之前，需要先清空画布，把上一次的内容清空
            this.ctx.clearRect(0, 0, windowSize.width, windowSize.height);

            // 绘制粒子
            this.particles.forEach(function (item) {
                item.draw(_this.ctx);
            });

            // 绘制粒子之间的连线
            for (let i = 0; i < this.particles.length; i++) {
                for (let j = i + 1; j < this.particles.length; j++) {
                    let distance = Math.sqrt(Math.pow(this.particles[i].x - this.particles[j].x, 2) + Math.pow(
                        this.particles[i].y - this.particles[j].y, 2));
                    if (distance < lineLenThreshold) {
                        // 这里我们让距离远的线透明度淡一点，距离近的线透明度深一点
                        this.ctx.strokeStyle = this.translateColors(this.config.color, (1 - distance /
                            lineLenThreshold));
                        this.ctx.beginPath();
                        this.ctx.moveTo(this.particles[i].x, this.particles[i].y);
                        this.ctx.lineTo(this.particles[j].x, this.particles[j].y);
                        this.ctx.closePath();
                        this.ctx.stroke();
                    }
                }
            }

            // 绘制粒子和鼠标之间的连线
            for (i = 0; i < this.particles.length; i++) {
                distance = Math.sqrt(Math.pow(this.particles[i].x - this.mouseCoordinates.x, 2) + Math.pow(this
                    .particles[i].y - this.mouseCoordinates.y, 2));
                if (distance < lineLenThreshold) {
                    this.ctx.strokeStyle = this.translateColors(this.config.color, (1 - distance /
                        lineLenThreshold));
                    this.ctx.beginPath();
                    this.ctx.moveTo(this.particles[i].x, this.particles[i].y);
                    this.ctx.lineTo(this.mouseCoordinates.x, this.mouseCoordinates.y);
                    this.ctx.closePath();
                    this.ctx.stroke();
                }
            }

            // 粒子移动，更新相应的x, y坐标
            this.move();

            // 循环调用draw方法
            window.requestAnimationFrame(this.draw.bind(this));
        },
        handleMouseMove: function (event) {

            let x, y;
            event = event || window.event;

            if (event.pageX || event.pageY) {
                x = event.pageX - document.documentElement.scrollLeft;
                y = event.pageY - document.documentElement.scrollTop;
            } else {
                x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
                y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
            }

            this.mouseCoordinates = {
                x: x,
                y: y
            };
        },
        handleWindowResize: function () {
            Utils.updateWindowSize();
            let windowSize = Utils.getWindowSize();
            this.canvas.width = windowSize.width;
            this.canvas.height = windowSize.height;
        },
        translateColors: function (colorStr, ratio) {

            let r, g, b, a = 1,
                colorValues;

            if (colorStr[0] === '#') { // 传的是#RRGGBB形式
                r = parseInt(colorStr.slice(1, 3), 16);
                g = parseInt(colorStr.slice(3, 5), 16);
                b = parseInt(colorStr.slice(5, 7), 16);
            } else if (colorStr.startsWith('rgb(')) { // 传的是rgb(r,g,b)形式
                colorStr = colorStr.slice(4, colorStr.length - 1);
                colorValues = colorStr.split(',');
                r = parseInt(colorValues[0].trim());
                g = parseInt(colorValues[1].trim());
                b = parseInt(colorValues[2].trim());
            } else if (colorStr.startsWith('rgba(')) { // 传的是rgba(r,g,b,a)形式
                colorStr = colorStr.slice(5, colorStr.length - 1);
                colorValues = colorStr.split(',');
                r = parseInt(colorValues[0].trim());
                g = parseInt(colorValues[1].trim());
                b = parseInt(colorValues[2].trim());
                a = parseFloat(colorValues[3].trim());
            }

            return 'rgba(' + r + ',' + g + ',' + b + ',' + a * ratio + ')';
        },
        run: function (config) {
            this.init(config);
        }
    };

    /**
     * Particle 粒子类
     */
    function Particle(attr) {

        // 粒子属性
        this.x = attr.x; // 粒子在画布中的横坐标
        this.y = attr.y; // 粒子在画布中的纵坐标
        this.vx = attr.vx; // 粒子的横向运动速度
        this.vy = attr.vy; // 粒子的纵向运动速度
        this.color = attr.color; // 粒子的颜色
        this.scale = attr.scale; // 粒子的缩放比例
        this.radius = attr.radius; // 粒子的半径大小

        // 绘制方法
        if (typeof Particle.prototype.draw === 'undefined') {
            Particle.prototype.draw = function (ctx) {
                // canvas画圆方法
                ctx.beginPath();
                ctx.fillStyle = this.color;
                ctx.strokeStyle = this.color;
                ctx.arc(this.x, this.y, this.radius * this.scale, 0, 2 * Math.PI, false);
                ctx.closePath();
                ctx.fill();
            }
        }
    }

    // 工具
    let Utils = {
        _windowSize: {
            width: 0,
            height: 0
        },
        getWindowSize: function () {
            return this._windowSize;
        },
        updateWindowSize: function () {
            this._windowSize.width = this.getWindowWidth();
            this._windowSize.height = this.getWindowHeight();
        },
        getWindowWidth: function () {
            return window.innerWidth || document.documentElement.clientWidth;
        },
        getWindowHeight: function () {
            return window.innerHeight || document.documentElement.clientHeight;
        },
        rangeRandom: function (min, max) {
            const diff = max - min;
            return min + Math.random() * diff;
        },
        supportRequestAnimationFrame: function () {
            if (!window.requestAnimationFrame) {
                window.requestAnimationFrame = (
                    window.webkitRequestAnimationFrame ||
                    window.mozRequestAnimationFrame ||
                    window.oRequestAnimationFrame ||
                    window.msRequestAnimationFrame ||
                    function (callback) {
                        setInterval(callback, 1000 / 60)
                    }
                );
            }
        }
    };
</script>